import { defaultToString } from "./utils";

export class ValuePair<K, V>{
    public key: K;
    public value: V;
    public constructor(key: K, value: V) {
        this.key = key;
        this.value = value;
    }
}


export class Dictionary<K, V>{

    private _table: { [key: string]: ValuePair<K, V> };

    public constructor() {
        this._table = {};
    }

    public set(key: K, value: V) {
        if (key != null && value != null) {
            const tableKey = defaultToString(key);
            this._table[tableKey] = new ValuePair(key, value);
            return true;
        }
        return false;
    }

    public remove(key: K) {
        if (this.hasKey(key)) {
            delete this._table[defaultToString(key)];
            return true;
        }
        return false;
    }

    public hasKey(key: K) {
        return this._table[defaultToString(key)] != null;
    }

    public get(key: K) {
        const valuePair = this._table[defaultToString(key)];
        return valuePair == null ? undefined : valuePair.value;
    }

    public values() {
        return this.keyValues().map(
            (valuePair: ValuePair<K, V>) => valuePair.value
        );
    }

    public keys(): K[] {
        return this.keyValues().map(
            (valuePair: ValuePair<K, V>) => valuePair.key
        );
    }

    public keyValues(): ValuePair<K, V>[] {
        // return Object.values(this._table);
        let values = [];
        for (let key in this._table) {
            if (this._table.hasOwnProperty(key)) {
                values.push(this._table[key]);
            }
        }
        return values;
    }

    public forEach(callbackFn: (key: K, value: V) => any) {
        const valuePairs = this.keyValues();
        for (let i = 0; i < valuePairs.length; i++) {
            const result = callbackFn(valuePairs[i].key, valuePairs[i].value);
            if (result === false) {
                break;
            }
        }
    }

    public clear() {
        this._table = {};
    }

    public toString() {
        if (this.length == 0) {
            return '';
        }
        const valuePairs = this.keyValues();
        let objString = `${valuePairs[0].toString()}`;
        for (let i = 1; i < valuePairs.length; i++) {
            objString = `${objString},${valuePairs[i].toString()}`;
        }
        return objString;
    }

    public get length() {
        return Object.keys(this._table).length;
    }
}
